package Redis

import (
	"errors"
	"fmt"
	"math"
)

/*
  分布式周期计数器，基于Redis实现
	Usage:
		pool, _ := CreatePool("127.0.0.1:6379", "password", 32, 32, 6, 3600, 0)
		counter := NewPeriodCounter(pool, 60, LocalTime{})
		num, err := counter.Incr("test", 1)

*/

type PeriodCounter struct {
	Pool       *RedisPool
	timeSlice  uint32 // 秒
	timeClient TimeClient
}

func NewPeriodCounter(pool *RedisPool, timeSlice uint32, timeClient TimeClient) (*PeriodCounter, error) {
	/*
		pool: redis连接池引用
		timeSlice：重新计数周期 单位秒
		timeClient：实现了TimeClient接口的结构体引用，主要是考虑到分布式环境下如果需要对秒级周期的计数
					因各分布式节点local time相差甚远，此时产生的计数结果将毫无意义
					所以此处预留接口，必要时使用分布式统一时间戳，例如阿里云的 ntp时间
	*/
	if timeSlice <= 0 {
		return nil, errors.New("timeSlice cannot less than 1")
	}

	periodCounterPtr := &PeriodCounter{pool, timeSlice, timeClient}

	return periodCounterPtr, nil

}

func (period *PeriodCounter) GetKey(key string) string {

	timePeriod := int(math.Floor(float64(period.timeClient.TimeStamp() / int64(period.timeSlice))))

	return fmt.Sprintf("%s_%d", key, timePeriod)
}

func (period *PeriodCounter) Get(key string) (interface{}, error) {
	key = period.GetKey(key)

	value, err := period.Pool.Get(key)

	return value, err
}

// Self-increasing value, The incrVal can be negative, it means self-decreasing behavior

func (period *PeriodCounter) Incr(key string, incrVal int) (int, error) {
	key = period.GetKey(key)
	return period.Pool.Incr(key, incrVal)
}
